package ua.yandex.collections;

public class MyLinkedList implements MyList {

    static class Node {

        private Object element;
        private Node next;
        private Node prev;

        Node(Object e, Node newPrev, Node newNext) {
            element = e;
            next = newNext;
            prev = newPrev;
        }
    }

    private Node first;
    private Node last;
    private int size;

    MyLinkedList() {
        first = null;
        last = null;
        size = 0;
    }

    MyLinkedList(Object[] c) {
        if (c.length == 0) {
            first = null;
            last = null;
            size = 0;
        } else {
            first = new Node(c[0], null, null);
            size = c.length;
            Node iterator = first;
            for (int i = 1; i < c.length; i++) {
                iterator.next = new Node(c[i], iterator, null);
                iterator = iterator.next;
            }
            last = iterator;
        }
    }

    public void addFirst(Object e) {
        if (size == 0) {
            first = new Node(e, null, null);
        } else {
            Node newFirst = new Node(e, null, first);
            first.prev = newFirst;
            first = newFirst;
        }
        size++;
    }

    public void addLast(Object e) {
        if (size == 0) {
            first = new Node(e, null, null);
        } else {
            Node newLast = new Node(e, last, null);
            last.next = newLast;
            last = newLast;
        }
        size++;
    }

    public Object getFirst() {
        if (size == 0) {
            return null;
        } else {
            return first.element;
        }
    }

    public Object getLast() {
        if (size == 0) {
            return null;
        } else {
            return last.element;
        }
    }

    public Object removeFirst() {
        if (size == 0) {
            throw new IndexOutOfBoundsException();
        }
        Object removed = first.element;
        size--;
        if (size == 0) {
            first = null;
            last = null;
        } else {
            first = first.next;
            first.prev = null;
        }
        return removed;
    }

    public Object removeLast() {
        if (size == 0) {
            throw new IndexOutOfBoundsException();
        }
        Object removed = last.element;
        size--;
        if (size == 0) {
            first = null;
            last = null;
        } else {
            last = last.prev;
            last.next = null;
        }
        return removed;
    }

    @Override
    public void add(Object e) {
        addLast(e);
    }

    @Override
    public void add(int index, Object e) {
        if (index > size || index < 0) {
            throw new IndexOutOfBoundsException();
        }
        if (index == 0) {
            addFirst(e);
        } else {
            if (index < size) {
                Node iterator = first;
                for (int i = 0; i < index - 1; i++) {
                    iterator = iterator.next;
                }
                Node newNode = new Node(e, iterator, iterator.next);
                size++;
                iterator.next = newNode;
                iterator.next.prev = newNode;
            } else {
                addLast(e);
            }
        }
    }

    @Override
    public void addAll(Object[] c) {
        for (int i = 0; i < c.length; i++) {
            addLast(c[i]);
        }
    }

    @Override
    public void addAll(int index, Object[] c) {
        if (index > size || index < 0) {
            throw new IndexOutOfBoundsException();
        }
        if (index == 0) {
            for (int i = c.length - 1; i >= 0; i--) {
                addFirst(c[i]);
            }
        } else {
            if (index < size) {
                Node iterator = first;
                for (int i = 0; i < index - 1; i++) {
                    iterator = iterator.next;
                }
                for (int i = 0; i < c.length; i++) {
                    Node newNode = new Node(c[i], iterator, iterator.next);
                    size++;
                    iterator.next = newNode;
                    iterator = iterator.next;
                }
                iterator.next.prev = iterator;
            } else {
                for (int i = 0; i < c.length; i++) {
                    addLast(c[i]);
                }
            }
        }
    }

    @Override
    public Object get(int index) {
        if (index >= size || index < 0) {
            throw new IndexOutOfBoundsException();
        }
        Node iterator = first;
        for (int i = 0; i < index; i++) {
            iterator = iterator.next;
        }
        return iterator.element;
    }

    @Override
    public Object remove(int index) {
        if (index >= size || index < 0) {
            throw new IndexOutOfBoundsException();
        }
        if (index == 0) {
            return removeFirst();
        } else {
            if (index == size - 1) {
                return removeLast();
            } else {
                Node iterator = first;
                for (int i = 0; i < index; i++) {
                    iterator = iterator.next;
                }
                size--;
                iterator.prev.next = iterator.next;
                iterator.next.prev = iterator.prev;
                return iterator.element;
            }
        }
    }

    @Override
    public void set(int index, Object e) {
        if (index >= size || index < 0) {
            throw new IndexOutOfBoundsException();
        }
        Node iterator = first;
        for (int i = 0; i < index; i++) {
            iterator = iterator.next;
        }
        iterator.element = e;
    }

    @Override
    public int indexOf(Object o) {
        Node iterator = first;
        int counter = 0;
        boolean b = true;
        while (b && counter < size) {
            if (iterator.element.equals(o)) {
                b = false;
            } else {
                iterator = iterator.next;
                counter++;
            }
        }
        if (b) {
            return -1;
        } else {
            return counter;
        }
    }

    @Override
    public int size() {
        return size;
    }

    @Override
    public void clear() {
        size = 0;
        first = null;
        last = null;
    }

    @Override
    public boolean isEmpty() {
        return size == 0;
    }

    @Override
    public Object[] toArray() {
        Object[] rez = new Object[size];
        Node iterator = first;
        for (int i = 0; i < size; i++) {
            rez[i] = iterator.element;
            iterator = iterator.next;
        }
        return rez;
    }

    @Override
    public String toString() {
        if (size == 0) {
            return "";
        } else {
            Node iterator = first;
            StringBuilder result = new StringBuilder(first.element.toString());
            for (int i = 1; i < size; i++) {
                iterator = iterator.next;
                result.append(",");
                result.append(iterator.element.toString());
            }
            return result.toString();
        }
    }
}
